package data

import (
	"context"
	"fmt"
	"gitee.com/min65535/gotechback/techback/conf"
	"gitee.com/min65535/gotechback/techback/util/json"
	redis "github.com/go-redis/redis/v8"
	log "github.com/sirupsen/logrus"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"time"
)

// Data 数据操作配置结构体
type Data struct {
	SqlConfig *conf.MysqlConfig
	RdbConfig *conf.RdbConfig
	DbSql     *gorm.DB
	// ReadUserSql *gorm.DB // 只读节点上的user数据库
	Rdb *redis.Client
}

// ConnectRedis redis连接方法
func ConnectRedis(cfg *conf.RedisConfig) *redis.Client {
	rdb := redis.NewClient(&redis.Options{
		Addr:     cfg.Addr,
		DB:       cfg.DB,       // use default DB
		PoolSize: cfg.PoolSize, // 连接池大小
		Password: cfg.Password,
	})

	ctx, cancel := context.WithTimeout(context.Background(), conf.CONN_TIMEOUT*time.Second)
	defer cancel()

	_, err := rdb.Ping(ctx).Result()

	if err != nil {
		log.Errorf("Failed to open redis with err(%v)", err)
		panic(err)
	}

	log.Debugf("ConnectRedis ok")

	return rdb
}

// ConnectDB 连接数据库方法
// WithGormConfig(WithGormConfigLog(WithSlowThreshold(1*time.Millisecond))) 设置打印的sql条件颜色等等
func ConnectDB(c *conf.MysqlDefaultConfig, opt ...gorm.Option) *gorm.DB {
	dns := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&timeout=%ds&readTimeout=%ds&writeTimeout=%ds",
		c.User, c.Password, c.Host, c.Port, c.DBName, c.Timeout, c.ReadTimeout, c.WriteTimeout)
	db, err := gorm.Open(mysql.Open(dns), opt...)
	if err != nil {
		panic(err)
	}

	sqlDB, err := db.DB()
	if err != nil {
		panic(err)
	}

	sqlDB.SetMaxIdleConns(c.MaxIdle)
	sqlDB.SetMaxOpenConns(c.MaxOpen)
	sqlDB.SetConnMaxLifetime(time.Duration(c.ConnMaxLifetime) * time.Hour)
	return db
}

func NewData(redisConfig *conf.RdbConfig, sqlConfig *conf.MysqlConfig) (*Data, func(), error) {
	log.Debugf("NewData#redisConfig:(%s), sqlConfig:(%s)", json.StringifyJson(redisConfig), json.StringifyJson(sqlConfig))
	data := &Data{
		RdbConfig: redisConfig,
		SqlConfig: sqlConfig,
	}

	data.Rdb = ConnectRedis(redisConfig.RDB)

	data.DbSql = ConnectDB(sqlConfig.TechDb)

	cleanup := func() {
		log.Info("msg", "closing the data resources")

		ddb, _ := data.DbSql.DB()
		defer ddb.Close()
		defer data.Rdb.Close()
	}
	return data, cleanup, nil
}
